package ltd.dujiabao.chat;


import java.io.Closeable;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;

/**
 * @author : DuJiabao
 * @Project : io_test
 * @Time : 2021/5/30 21:00
 * @Desc :
 */
public class Server {
  private final Integer PORT;
  private ServerSocketChannel serverSocketChannel;
  private Selector selector;

  public Server(Integer PORT) {
    this.PORT = PORT;
    try {
      serverSocketChannel = ServerSocketChannel.open();
      serverSocketChannel.configureBlocking(false);
      serverSocketChannel.bind(new InetSocketAddress(this.PORT));
      selector = Selector.open();
      serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
      System.out.println("开启服务器成功");
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

  /**
   * 开始监听任务，包括处理连接、处理消息
   */
  private void listen() {
    System.out.println("开始监听");
    try {
      // 进入轮询状态
      while (selector.select() > 0) {  // 当通道大于0
        Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
        while (iterator.hasNext()) {  // 如果有任务来了就进行，否则就阻塞
          System.out.println("有任务来了");
          SelectionKey selectionKey = iterator.next();
          if (selectionKey.isAcceptable()) {  // 如果是请求连接任务来了
            SocketChannel newChannel = serverSocketChannel.accept(); // 终究还是要接收的，只是现在才进入接收请求
            System.out.println("有新的连接过来了:" + newChannel.getRemoteAddress());
            newChannel.configureBlocking(false);// 开启非阻塞模式
            newChannel.register(selector, SelectionKey.OP_READ); // 将这个新通道放进选择器中
          } else if (selectionKey.isReadable()) {  // 如果是写任务来了
            tackleIssue(selectionKey);
          }
          // 处理完之后还要删了它
          iterator.remove();
        }
      }
    } catch (IOException e) {
      e.printStackTrace();
    } finally {
      closeResource(selector);
      closeResource(serverSocketChannel);
    }
  }

  /**
   * 用于关闭资源
   * @param closeable 接口
   */
  private static void closeResource(Closeable closeable) {
    if (closeable != null) {
      try {
        closeable.close();
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
  }

  /**
   * 处理消息任务
   * @param selectionKey
   */
  private void tackleIssue(SelectionKey selectionKey) {
    SocketChannel channel = (SocketChannel) selectionKey.channel();
    ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
    try {
      while ((channel.read(byteBuffer)) > 0) {
        byteBuffer.flip();
        String msg = new String(byteBuffer.array(), 0, byteBuffer.remaining());
        System.out.println("服务端接收到 " + channel.getRemoteAddress() + " 的信息：" + msg);
        sendToOtherClients(byteBuffer, channel);
        byteBuffer.clear();
      }
    } catch (IOException e) {
      try {
        System.out.println(channel.getRemoteAddress() + "离线了...");
        selectionKey.cancel(); // 在选择器中取消注册
        closeResource(channel); // 关闭通道
      } catch (IOException ioException) {
        ioException.printStackTrace();
      }
      e.printStackTrace();
    }
  }

  /**
   * 将消息转发给其他客户端
   * @param byteBuffer
   * @param channel
   * @throws IOException
   */
  private void sendToOtherClients(ByteBuffer byteBuffer, SocketChannel channel) throws IOException {
    System.out.println("发送给其他客户端");
    for (SelectionKey key : selector.keys()) {  // 遍历在selector中的所有通道
      SelectableChannel dst = key.channel();
      if (dst instanceof SocketChannel && dst != channel) {  // 如果通道不是发送的通道，也不是处理连接请求的通道，就发一份给他
        byteBuffer.mark();  // 记住当前的位置
        ((SocketChannel) dst).write(byteBuffer);  // 疯狂输出给其他客户端
        byteBuffer.reset();  // 恢复回去
      }
    }
  }


  public static void main(String[] args) {
    Server server = new Server(8080);
    server.listen();
  }
}
